package com.cloudkinto.service.user.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.common.DicBusinessConstant;
import com.cloudkinto.common.DicBusinessItemConstant;
import com.cloudkinto.common.common.DateUtils;
import com.cloudkinto.common.common.HttpUtil;
import com.cloudkinto.common.common.MD5Util;
import com.cloudkinto.common.common.VerificationCodeUtil;
import com.cloudkinto.common.constant.GlobalConstants;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.OrderCodeUtil;
import com.cloudkinto.common.utils.XmlUtils;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.*;
import com.cloudkinto.entity.*;
import com.cloudkinto.service.file.FileService;
import com.cloudkinto.service.role.RoleService;
import com.cloudkinto.service.user.UserService;
import com.cloudkinto.service.user.vo.*;
import com.cloudkinto.service.user.wxvo.ScanningCodeRes;
import com.cloudkinto.service.user.wxvo.WxTemplatePushReq;
import com.cloudkinto.session.UserSession;
import com.cloudkinto.session.UserSessionException;
import com.cloudkinto.session.UserSessionManager;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * service实现类
 * </p>
 *
 * @author 张永远
 * @since 2020-05-27
 */
@Service
@Transactional(rollbackFor = Exception.class)//事务回滚
public class UserServiceImpl extends ServiceImpl<UserDao, UserDo> implements UserService {
    @Value("${gbc.login.verificationCode}")
    private boolean verificationCode;

    @Value("${gbc.scheduling}")
    private String type;

    @Autowired
    private UserDao dao;
    @Autowired
    private FileService fileService;
    @Autowired
    private CompanyDao companyDao;
    @Autowired
    private FileDao fileDao;
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private MenuDao menuDao;
    @Autowired
    private RoleService roleService;
    @Autowired
    private ShopDao shopDao;
    @Autowired
    private DicBusinessItemDao dicBusinessItemDao;
    @Autowired
    private UserSessionManager userSessionManager;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxUserDao wxUserDao;
    @Autowired
    private RoleMenuRelationDao roleMenuRelationDao;

    @Override
    public UserLoginSuccessRes login(UserLoginReq req, HttpServletRequest request) {
        //校验验证码
        if (verificationCode) {
            String verificationCode = (String) redisTemplate.opsForValue().get("verificationCode:" + req.getKey());
            if (StringUtils.isBlank(verificationCode)) {
                throw new BizException(SysConstant.Login_VerCode_Expired);
            } else if (!(verificationCode.equals(req.getVerificationCode()))) {
                throw new BizException(SysConstant.Login_VerCode_InCorrect);
            }
        }
        MD5Util md5 = new MD5Util(SysConstant.SALT_STRING, "MD5");

        LambdaQueryWrapper<UserDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserDo::getUserAccount, req.getUserAccount())
                .eq(UserDo::getUserPassword, md5.encode(req.getUserPassword()))
                .eq(UserDo::getUserStatus, 1)
                .eq(UserDo::getUserType, 2);

        UserDo userDo = this.baseMapper.selectOne(wrapper);
        if (userDo == null) {
            throw new BizException(SysConstant.Login_Error_AccountPassword);
        } else if (userDo.getUserType() != 2) {
            throw new BizException(SysConstant.Error_Account_NoPower);
        }

        //创建会话
        userDo.setCompanyId(req.getCompanyId());
        UserSession session = userSessionManager.createSession(userDo);

        UserLoginSuccessRes res = new UserLoginSuccessRes();
        res.setUserId(session.getUserId());
        res.setSessionId(session.getSessionId());
        res.setUserAccount(userDo.getUserAccount());
        res.setCompanyId(req.getCompanyId());
        res.setUserName(userDo.getUserName());
        res.setHeadUrl(fileService.getFilePath(userDo.getHeadFileId()));
        String lang = request.getHeader("lang");
        res.setMenuTree(roleService.getMenuTree(userDo.getRoleId(), lang != null && lang.equals("jp") ? 2 : 1, req.getCompanyId()));
        res.setButton(menuDao.getButton(userDo.getRoleId()));
        //登录成功 session域中改变登陆人
        request.getSession().setAttribute(UserSession.SESSION_ID_KEY, session);
        return res;
    }

    @Override
    public UserLoginSuccessRes main(String token) {
        UserSession session = userSessionManager.getSession(token);
        if (session == null) throw new UserSessionException();
        UserLoginSuccessRes res = new UserLoginSuccessRes();
        UserDo userDo = dao.selectById(session.getUserId());
        if (userDo == null) throw new BizException("用户不存");
        res.setUserId(session.getUserId());
        res.setSessionId(session.getSessionId());
        res.setUserAccount(userDo.getUserAccount());
        res.setUserName(userDo.getUserName());
        res.setHeadUrl(fileService.getFilePath(userDo.getHeadFileId()));
        res.setMenuTree(roleService.getMenuTree(userDo.getRoleId(), 1, userDo.getCompanyId()));
        res.setButton(menuDao.getButton(userDo.getRoleId()));
        return res;
    }

    @Override
    public Object pdaLogin(UserPdaLoginReq req) {
        LambdaQueryWrapper<UserDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserDo::getUserAccount, req.getUserAccount())
                .eq(UserDo::getUserPassword, req.getUserPassword())
                .eq(UserDo::getUserStatus, 1);

        UserDo userDo = this.baseMapper.selectOne(wrapper);
        if (userDo == null) {
            throw new BizException(SysConstant.Login_Error_AccountPassword);
        } else if (userDo.getUserType() != 2) {
            throw new BizException(SysConstant.Error_Account_NoPower);
        }

        //创建会话
        UserSession session = userSessionManager.createSession(userDo);
        UserLoginSuccessRes res = new UserLoginSuccessRes();
        res.setUserId(session.getUserId());
        res.setSessionId(session.getSessionId());
        res.setUserAccount(userDo.getUserAccount());
        res.setUserName(userDo.getUserName());
        res.setHeadUrl(fileService.getFilePath(userDo.getHeadFileId()));
        return res;
    }

    @Override
    public void logout(String token) {
        userSessionManager.clearSession(token);
    }

    @Override
    public UserRegisterReq register(UserRegisterReq req) {
        checkRegister(req);

        if (req.getCompanyFileId() != null) {
            fileService.uploadFile(req.getCompanyFileId());
        }

        CompanyDo companyDo = new CompanyDo();
        companyDo.setName(req.getCompanyName());
        companyDo.setFileId(req.getCompanyFileId());
        companyDo.setCreateTime(new Date());
        companyDo.setDeleteFlag(SysConstant.DELETE_FLAG_FALSE);
        companyDao.insert(companyDo);
        companyDo.setCompanyCode(OrderCodeUtil.generateCompanyCode(companyDo.getId()));
        companyDao.updateById(companyDo);

        MD5Util md5 = new MD5Util(SysConstant.SALT_STRING, "MD5");

        UserDo userDo = new UserDo();
        userDo.setUserPassword(md5.encode(req.getUserPassword()));
        userDo.setUserAccount(req.getUserAccount());
        userDo.setUserName(req.getUserName());
        userDo.setUserMail(req.getUserMail());
        userDo.setUserPhone(req.getUserPhone());
        userDo.setCompanyId(companyDo.getId());
        userDo.setUserType(2);
        userDo.setUserStatus(0);
        userDo.setCreateTime(new Date());
        userDo.setDeleteFlag(SysConstant.DELETE_FLAG_FALSE);

        //生成权限
        long id = this.createDefaultRole(companyDo.getId());
        userDo.setRoleId(id);
        dao.insert(userDo);
        return req;
    }


    /**
     * 给公司生成默认角色 和权限
     * @param companyId
     * @return
     */
    public long createDefaultRole(Long companyId) {
        //创建公司默认角色
        List<RoleDo> roleDoList = roleDao.selectList(new LambdaQueryWrapper<RoleDo>().eq(RoleDo::getCompanyId, -1).isNotNull(RoleDo::getDevCode).eq(RoleDo::getRoleType, 1));
        long adminRoleId = 0;
        for (RoleDo role : roleDoList) {
            //先去判断有没有
            RoleDo et = roleDao.selectOne(new LambdaQueryWrapper<RoleDo>().eq(RoleDo::getCompanyId, companyId).eq(RoleDo::getDevCode, role.getDevCode()).last(" limit 1"));
            if (et == null) {
                et = new RoleDo();
                BeanUtils.copyProperties(role, et);
                et.setId(null);
                et.setCompanyId(companyId);
                roleDao.insert(et);
                if (et.getDevCode().equals(SysConstant.clientAdmin)) {
                    adminRoleId = et.getId();
                }
                List<RoleMenuRelationDo> list = roleMenuRelationDao.selectList(new LambdaQueryWrapper<RoleMenuRelationDo>().eq(RoleMenuRelationDo::getRoleId, role.getId()));
                for (RoleMenuRelationDo roleMenuDo : list) {
                    RoleMenuRelationDo relationDo = new RoleMenuRelationDo();
                    relationDo.setRoleId(et.getId());
                    relationDo.setMenuId(roleMenuDo.getMenuId());
                    roleMenuRelationDao.insert(relationDo);
                }
            }
            //这个公司下的 用户 roleId 改成新的
            LambdaQueryWrapper<UserDo> userWrapper = new LambdaQueryWrapper<>();
            if (companyId == -1L) {
                userWrapper.isNull(UserDo::getCompanyId);
            }
            else {
                userWrapper.eq(UserDo::getCompanyId, companyId);
            }
            userWrapper.eq(UserDo::getRoleId, role.getId());
            List<UserDo> userDos = dao.selectList(userWrapper);
            for (UserDo userDo : userDos) {
                userDo.setRoleId(et.getId());
                dao.updateById(userDo);
            }
//            if (et.getDevCode().equals(SysConstant.clientAdmin) || et.getDevCode().equals(SysConstant.serverAdmin)) {
                //如果是管理员 则 给所有按钮加权限
                List<MenuDo> button = menuDao.selectList(new LambdaQueryWrapper<MenuDo>().eq(MenuDo::getType, 3).eq(MenuDo::getSystem, 0));
                for (MenuDo menuDo : button) {
                    RoleMenuRelationDo relationDo = roleMenuRelationDao.selectOne(new LambdaQueryWrapper<RoleMenuRelationDo>().eq(RoleMenuRelationDo::getMenuId, menuDo.getId())
                            .eq(RoleMenuRelationDo::getRoleId, et.getId()).last(" order by id desc limit 1"));
                    if (relationDo != null) {
                        continue;
                    }
                    relationDo = new RoleMenuRelationDo();
                    relationDo.setRoleId(et.getId());
                    relationDo.setMenuId(menuDo.getId());
                    roleMenuRelationDao.insert(relationDo);
                }
//            }
        }
        return adminRoleId;
    }


    /**
     * 角色系统变更， 默认都变成 管理员
     */
    @Override
    public void createRoleForCompany() {
        List<CompanyDo> companyDoList = companyDao.selectList(null);
        for (CompanyDo et : companyDoList) {
            this.createDefaultRole(et.getId());
        }
        this.createDefaultRole(-1L);
    }


    private void checkRegister(UserRegisterReq req) {
        LambdaQueryWrapper<UserDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserDo::getUserAccount, req.getUserAccount());
        int count = this.baseMapper.selectCount(wrapper);
        if (count > 0) {
            throw new BizException(SysConstant.Login_Error_Existed);
        }
    }


    @Override
    public SingleResult bindKeyInit() {

        List<Map> platformList = new ArrayList<>();

        LambdaQueryWrapper<DicBusinessItemDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DicBusinessItemDo::getDicBusinessId, DicBusinessConstant.platform);

        List<DicBusinessItemDo> itemDoList = dicBusinessItemDao.selectList(wrapper);
        if (CollectionUtils.isNotEmpty(itemDoList)) {
            for (DicBusinessItemDo itemDo : itemDoList) {
                Map map = new HashMap();
                map.put("platformId", itemDo.getId());
                map.put("value", itemDo.getDicItemValue());
                platformList.add(map);
            }
        }

        return SingleResult.success(platformList);
    }

    @Override
    public Object detail(Long id, Long userId) {
        return dao.selectById(id);
    }


    @Override
    public SingleResult keySecret(Long plantFormId, Long shopId) {
        Map map = new HashMap();
        ShopDo shopDo = shopDao.selectById(shopId);
        if (shopDo != null) {
            if (plantFormId.equals(DicBusinessItemConstant.amazon)) {
                map.put("key", shopDo.getAmazonSellerId());
                map.put("value", shopDo.getAmazonToken());
                map.put("keyName", "卖家账号");
                map.put("valueName", "访问token");
            } else if (plantFormId.equals(DicBusinessItemConstant.rakuton)) {
                map.put("key", shopDo.getRakutenLicense());
                map.put("value", shopDo.getRakutenSecret());
                map.put("keyName", "licenseKey");
                map.put("valueName", "服务秘钥（serviceSecret）");
            }
        }
        return SingleResult.success(map);
    }

    @Override
    public SingleResult shopListFromPlantForm(Long plantFormId) {

        List<Map> shopList = new ArrayList<>();
        LambdaQueryWrapper<ShopDo> shopDoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        shopDoLambdaQueryWrapper.eq(ShopDo::getPlatformId, plantFormId);
        List<ShopDo> shopDoList = shopDao.selectList(shopDoLambdaQueryWrapper);
        if (!CollectionUtils.isEmpty(shopDoList)) {
            for (ShopDo shopDo : shopDoList) {
                Map itemMap = new HashMap();
                itemMap.put("shopId", shopDo.getId());
                itemMap.put("value", shopDo.getShopName());
                shopList.add(itemMap);
            }
        }
        return SingleResult.success(shopList);
    }

    @Override
    public SingleResult addInit() {
        List<Map> result = new ArrayList<>();

        LambdaQueryWrapper<RoleDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoleDo::getRoleType, 2);
        wrapper.eq(RoleDo::getCompanyId, -1L);
        List<RoleDo> roleDoList = roleDao.selectList(wrapper);
        if (CollectionUtils.isNotEmpty(roleDoList)) {
            for (RoleDo roleDo : roleDoList) {
                Map map = new HashMap();
                map.put("id", roleDo.getId());
                map.put("value", roleDo.getRoleName());
                result.add(map);
            }
        }
        return SingleResult.success(result);
    }

    @Override
    public UserBuildReq add(UserBuildReq req, Long userId, Long companyId) {
        addCheck(req);

        MD5Util md5 = new MD5Util(SysConstant.SALT_STRING, "MD5");

        UserDo entityDo = new UserDo();
        BeanUtils.copyProperties(req, entityDo);
        handlerAddBefore(entityDo, userId);

        entityDo.setUserPassword(md5.encode(req.getUserPassword()));
        entityDo.setUserStatus(1);
        entityDo.setUserType(2);
        entityDo.setCompanyId(companyId);
        dao.insert(entityDo);
        return req;
    }

    @Override
    public void check(UserCheckReq req, Long userId) {
        UserDo userDo = dao.selectById(req.getId());
        userDo.setUserStatus(req.getStatus());
        userDo.setUpdateBy(userId);
        userDo.setUpdateTime(new Date());
        dao.updateById(userDo);
    }

    //新增校验
    private void addCheck(UserBuildReq req) {
        LambdaQueryWrapper<UserDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserDo::getUserAccount, req.getUserAccount());
        int count = this.baseMapper.selectCount(wrapper);
        if (count > 0) {
            throw new BizException(SysConstant.Login_Error_Existed);
        }
    }

    //做新增之前要执行的业务定制处理
    private void handlerAddBefore(UserDo entityDo, Long userId) {
        entityDo.setCreateBy(userId);
        entityDo.setCreateTime(new Date());
        entityDo.setDeleteFlag(SysConstant.DELETE_FLAG_FALSE);
    }


    @Override
    public SingleResult updateInit(Long id) {
        UserDo entityDo = dao.selectById(id);
        if (entityDo == null) {
            throw new BizException(SysConstant.User_NotExist);
        }
        UserDetailRes res = new UserDetailRes();

        res.setId(entityDo.getId());
        res.setUserAccount(entityDo.getUserAccount());
        res.setUserName(entityDo.getUserName());
        res.setUserMail(entityDo.getUserMail());
        res.setUserPhone(entityDo.getUserPhone());
        res.setIsBinding(entityDo.getIsBinding());

        CompanyDo companyDo = companyDao.selectById(entityDo.getCompanyId());
        if (companyDo != null) {
            res.setCompanyName(companyDo.getName());
        }
        FileDo fileDo = fileDao.selectById(entityDo.getHeadFileId());
        if (fileDo != null) {
            res.setHeadFileUrl(fileService.getFilePath(fileDo.getId()));
            res.setHeadFileId(fileDo.getId());
            res.setFileName(fileDo.getFileName());
        }

        return SingleResult.success(res);
    }

    @Override
    public UserUpdateReq update(UserUpdateReq req, Long userId) {
        UserDo entityDo = dao.selectById(req.getId());

        if (req.getHeadFileId() != null) {
            fileService.deleteFile(entityDo.getHeadFileId());
            fileService.uploadFile2(req.getHeadFileId(), entityDo.getHeadFileId());
        }
        entityDo.setUserPhone(req.getUserPhone());
        entityDo.setUserMail(req.getUserMail());
        entityDo.setHeadFileId(req.getHeadFileId());
        entityDo.setIsBinding(req.getIsBinding());
        handlerUpdateBefore(entityDo, userId);
        dao.updateById(entityDo);
        return req;
    }

    @Override
    public UpdatePasswordReq updatePassword(UpdatePasswordReq req, Long userId) {
        if (!req.getNewPassword().equals(req.getNewPasswordAgain())) {
            throw new BizException(SysConstant.Login_Update_NotSame);
        }
        UserDo entityDo = dao.selectById(userId);
        MD5Util md5 = new MD5Util(SysConstant.SALT_STRING, "MD5");
        if (!(md5.encode(req.getPassword()).equals(entityDo.getUserPassword()))) {
            throw new BizException(SysConstant.Login_Error_OldPass);
        }

        entityDo.setUserPassword(md5.encode(req.getNewPassword()));
        handlerUpdateBefore(entityDo, userId);
        dao.updateById(entityDo);
        return req;
    }

    @Override
    public SingleResult bindKey(UserBindReq req, Long userId) {
        LambdaQueryWrapper<ShopDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ShopDo::getId, req.getShopId()).eq(ShopDo::getPlatformId, req.getPlatformId());
        ShopDo shopDo = shopDao.selectOne(wrapper);
        if (shopDo == null) {
            throw new BizException(SysConstant.User_Error_BingKey);
        }
        if (req.getPlatformId().equals(DicBusinessItemConstant.amazon)) {
            shopDo.setAmazonSellerId(req.getKey());
            shopDo.setAmazonToken(req.getValue());
        } else if (req.getPlatformId().equals(DicBusinessItemConstant.rakuton)) {
            shopDo.setRakutenLicense(req.getKey());
            shopDo.setRakutenSecret(req.getValue());
        }
        shopDo.setUpdateBy(userId);
        shopDo.setUpdateTime(new Date());
        shopDao.updateById(shopDo);
        return SingleResult.success(SysConstant.User_Bind_Success, req);
    }

    //编辑校验
    private void updateCheck(Object req) {

    }

    //做修改之前要执行的业务定制处理
    private void handlerUpdateBefore(UserDo entityDo, Long userId) {
        entityDo.setUpdateBy(userId);
        entityDo.setUpdateTime(new Date());
    }


    @Override
    public int delete(Long id, Long userId) {
        UserDo entityDo = dao.selectById(id);
        handlerDeleteBefore(entityDo, userId);
        dao.updateById(entityDo);
        dao.updateById(entityDo);
        return dao.deleteById(id);
    }

    //做删除之前要执行的业务定制处理
    private void handlerDeleteBefore(UserDo entityDo, Long userId) {
        entityDo.setDeleteBy(userId);
        entityDo.setDeleteTime(new Date());
    }

    @Override
    public SingleResult verificationCode() {
        Map map = VerificationCodeUtil.generateCode();
        String key = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set("verificationCode:" + key, map.get("result"), 5 * 60, TimeUnit.SECONDS);
        map.remove("result");
        map.put("key", key);
        return SingleResult.success(map);
    }

    //查询明细之后要执行的业务定制处理
    private Object handlerDetailAfter(Object res) {
        return res;
    }

    @Override
    public SingleResult pageInit() {
        return SingleResult.success("");
    }

    @Override
    public List<UserListPageRes> getList(Map<String, String> map) {
        QueryWrapper<UserDo> wrapper = queryBuild(map);
        List<UserDo> list = this.list(wrapper);
        List<UserListPageRes> result = handlerListAfter(list);
        return result;
    }

    @Override
    public PageResult getListPage(Map<String, String> map) {
        QueryWrapper<UserDo> wrapper = queryBuild(map);
        IPage<UserDo> page = pageInit(map);
        page = this.page(page, wrapper);
        List<UserListPageRes> resList = handlerListAfter(page.getRecords());
        PageResult result = new PageResult(resList, (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
        return result;
    }

    //查询分页列表之后对列表数据的处理
    private List<UserListPageRes> handlerListAfter(List<UserDo> list) {
        List<UserListPageRes> result = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)) {
            for (UserDo userDo : list) {
                UserListPageRes res = new UserListPageRes();
                res.setUserAccount(userDo.getUserAccount());
                res.setUserName(userDo.getUserName());
                res.setId(userDo.getId());
                res.setUserStatus(userDo.getUserStatus());
                res.setUserMail(userDo.getUserMail());
                res.setUserPhone(userDo.getUserPhone());
                res.setUserType(userDo.getUserType());
                if (userDo.getUserType() != null)
                    res.setUserTypeName(userDo.getUserType() == 1 ? "客户" : "后台用户");
                if (userDo.getUserStatus() == 0) {
                    res.setUserStatusName("未审核");
                } else if (userDo.getUserStatus() == 1) {
                    res.setUserStatusName("通过");
                } else {
                    res.setUserStatusName("禁用");
                }
                RoleDo roleDo = roleDao.selectById(userDo.getRoleId());
                if (roleDo != null) {
                    res.setRoleName(roleDo.getRoleName());
                }
                CompanyDo companyDo = companyDao.selectById(userDo.getCompanyId());
                if (companyDo != null) {
                    res.setCompanyCode(companyDo.getCompanyCode());
                }
                result.add(res);
            }
        }
        return result;
    }


    private IPage<UserDo> pageInit(Map<String, String> map) {
        IPage<UserDo> page = new Page();
        page.setSize(StringUtils.isNotBlank(map.get("pageSize")) ? Long.parseLong(map.get("pageSize")) : 10);
        page.setCurrent(StringUtils.isNotBlank(map.get("current")) ? Long.parseLong(map.get("current")) : 1);
        return page;
    }

    private QueryWrapper<UserDo> queryBuild(Map<String, String> map) {
        QueryWrapper<UserDo> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(map.get("companyId"))) {
            wrapper.eq("company_id", Long.parseLong(map.get("companyId")));
        }
        if (StringUtils.isNotBlank(map.get("keyword"))) {
            wrapper.and(i -> i.like("user_account", map.get("keyword"))
                    .or().like("user_name", map.get("keyword"))
                    .or().like("user_phone", map.get("keyword")));
        }
        wrapper.orderByDesc("id");
        return wrapper;
    }

    /**************************微信公众号功能区*************************/
    /**
     * 创建临时带参数二维码
     * scene_id 场景Id  777:登录二维码场景id    888:绑定二维码场景id
     */
    @Override
    public String createTicket(Long userId) {
        //调用静态方法获取access_token
        String accessToken = getAccessToken();
        if (StringUtils.isBlank(accessToken)) {
            accessToken = getAccessToken();
        }
        TreeMap<String, String> params = new TreeMap<>();
        params.put("access_token", accessToken);
        // output data
        JsonObject data = new JsonObject();
        data.addProperty("action_name", "QR_SCENE");
        data.addProperty("expire_seconds", 300);
        JsonObject scene = new JsonObject();
        //只有登录才能获取到userId 所以有userId就是获取绑定二维码
        if (userId == null) {
            scene.addProperty("scene_id", "777");
        } else {
            scene.addProperty("scene_id", "888");
        }
        JsonObject actionInfo = new JsonObject();
        actionInfo.add("scene", scene);
        data.add("action_info", actionInfo);
        log.debug("二维码请求参数：" + data);
        String result = HttpUtil.doPost("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=", params, data.toString());
        //调用微信接口返回的result是json格式 将json转为object
        String body = JSONObject.toJSONString(result);
        Object ob = JSON.parse(body);
        String s = ob.toString();
        log.debug("生成二维码返回数据：" + s);
        //add 2022 03 21
        JSONObject jb = JSONObject.parseObject(s);
        String errCode = jb.getString("errcode");
        if (StringUtils.isNotBlank(errCode)) {
            if ("40001".equals(errCode)) {
                redisTemplate.opsForValue().set("logistics_wx_token", null, 2, TimeUnit.HOURS);
            }
        }
        WechatQRCode qrCode = JSONObject.parseObject(s, WechatQRCode.class);
        Map map = new HashMap();
        //将userId存放redis中，当扫描绑定二维码时，和xml数据包里的openId一起存在本地数据库,时效【5分钟】
        if (userId != null) {
            map.put("userId", String.valueOf(userId));
            redisTemplate.opsForValue().set("logistics_wx_binding_ticket:" + qrCode.getTicket(), map, 5, TimeUnit.MINUTES);
        }
        //判断token是否被替换掉 替换掉拿新的token生成ticket
        if (StringUtils.isBlank(qrCode.getTicket())) {
            accessToken = getAccessToken();
            if (!type.equals("dev")) {
                String ticket = createTicket(userId);
                System.out.println("***********" + accessToken);
                return ticket;
            } else {
                return "二维码已失效";
            }
        } else {
            return qrCode == null ? "二维码已失效" : qrCode.getTicket();
        }
    }

    /**
     * type 区分二维码类型 0：普通二维码 1：绑定二维码
     * status:1 已扫描
     * eventKey含有777：关注二维码  含有888：绑定二维码
     */
    @Override
    public String scanningCode(String request) {
        try {
            JSONObject jsonObject = XmlUtils.xml2Json(request);
            JSONObject jb = (JSONObject) jsonObject.get("xml");
            if (jb == null) {
                throw new BizException(SysConstant.User_Scanning_NoXml);
            }
            log.error("微信xml数据包内容：" + jb.toJSONString());
            System.err.println("========微信xml数据包内容：" + jb.toJSONString());
            Map map = new HashMap();
            String ticket = jb.getString("Ticket");
            //未关注时，eventKey有前缀 qrscene_
            String eventKey = jb.getString("EventKey");
            //非事件操作 没有eventKey 即向公众号发送消息时
            if (StringUtils.isBlank(eventKey)) {
                return "success";
            }
            if (eventKey.contains("qrscene_")) {
                eventKey = eventKey.replace("qrscene_", "");
            }
            map.put("msgType", jb.getString("MsgType"));
            map.put("event", jb.getString("Event"));
            map.put("openId", jb.getString("FromUserName"));
            map.put("status", 1);
            //获取openId用于保存到本地数据库
            WxUserDo wxdo = wxUserDao.selectOne(new QueryWrapper<WxUserDo>().eq("openid", jb.getString("FromUserName")));
            WxUserDo wxUserDo = new WxUserDo();
            wxUserDo.setOpenid(jb.getString("FromUserName"));
            //关注二维码数据包
            if (eventKey.equals("777")) {
                map.put("type", 0);
                //获取微信xml数据包表示二维码已经扫描，将二维码ticket存在redis中，过期时间【5分钟】
                redisTemplate.opsForValue().set("logistics_wx_login_ticket:" + ticket, map, 5, TimeUnit.MINUTES);
                //判断openId在数据库中是否存在，存在更新，不存在插入
                if (wxdo != null) {
                    wxUserDo.setId(wxdo.getId());
                    wxUserDo.setUpdateTime(new Date());
                    wxUserDao.updateById(wxUserDo);
                } else {
                    wxUserDo.setCreateTime(new Date());
                    wxUserDao.insert(wxUserDo);
                }

                //判断是否扫码登录进入 成功则公众号通知用户  add 2022.2.25 这个if()只是用来处理登录成功微信通知功能
                if (redisTemplate.hasKey("logistics_wx_login_ticket:" + ticket)) {
                    Map map1 = (Map) redisTemplate.opsForValue().get("logistics_wx_login_ticket:" + ticket);
                    if (null != map1) {
                        //从redis中取openId，然后查到微信用户信息，然后取userId,再取到userName,userPassword
                        String openId = (String) map1.get("openId");
                        //redis序列化
                        Integer status = (Integer) map1.get("status");
                        if (status == 1) {
                            WxUserDo wxUserDo1 = wxUserDao.selectOne(new QueryWrapper<WxUserDo>().eq("openid", openId));
                            UserDo userDo = dao.selectById(wxUserDo1.getUserId());
                            if (userDo == null || userDo.getIsBinding() != 1) {
                                //进这一步说明用户没绑定
                                System.out.println("没有绑定");
                            } else {
                                //到这一步说明用户扫码登录成功进去系统了 公众号提示
                                JSONObject jb1 = new JSONObject();
                                jb1.put("ToUserName", wxUserDo.getOpenid());
                                jb1.put("FromUserName", "gh_0025fe6e1c4d");
                                jb1.put("CreateTime", System.currentTimeMillis() / 1000);
                                jb1.put("MsgType", "text");
                                jb1.put("Content", "扫码登录成功！");
                                String s = XmlUtils.JsonToXml(jb1);
                                log.debug("********" + s);
                                System.err.println("*********" + s);
                                return s;
                            }
                        }
                    }
                }
            } else if (eventKey.equals("888")) {
                map.put("type", 1);
                if (redisTemplate.hasKey("logistics_wx_binding_ticket:" + ticket)) {
                    Map map1 = (Map) redisTemplate.opsForValue().get("logistics_wx_binding_ticket:" + ticket);
                    if (null != map1) {
                        //获取微信xml数据包表示二维码已经扫描，将二维码ticket存在redis中，过期时间【5分钟】
                        redisTemplate.opsForValue().set("logistics_wx_binding_ticket:" + ticket, map, 5, TimeUnit.MINUTES);
                        wxUserDo.setUserId(Long.valueOf((String) map1.get("userId")));
                        //判断openId在数据库中是否存在，存在更新，不存在插入
                        if (wxdo != null) {
                            wxUserDo.setId(wxdo.getId());
                            wxUserDo.setUpdateTime(new Date());
                            wxUserDao.updateById(wxUserDo);
                        } else {
                            wxUserDo.setCreateTime(new Date());
                            wxUserDao.insert(wxUserDo);
                        }
                        //扫描二维码就绑定了，将user表中的is_binding 更新为1
                        UserDo userDo = new UserDo();
                        userDo.setId(Long.valueOf((String) map1.get("userId")));
                        userDo.setIsBinding(1);
                        dao.updateById(userDo);

                        //绑定后发送绑定成功通知给用户    add 2022.2.25
                        JSONObject jb1 = new JSONObject();
                        jb1.put("ToUserName", wxUserDo.getOpenid());
                        jb1.put("FromUserName", "gh_0025fe6e1c4d");
                        jb1.put("CreateTime", System.currentTimeMillis() / 1000);
                        jb1.put("MsgType", "text");
                        jb1.put("Content", "恭喜你，绑定成功！");
                        String s = XmlUtils.JsonToXml(jb1);
                        log.debug("********" + s);
                        System.err.println("*********" + s);
                        return s;
                    }
                } else {
                    return "二维码已失效！";
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }

    /**
     * 判断缓存里是否有保存的ticket，若存在说明二维码已扫描，反之。
     */
    @Override
    public SingleResult whetherScan(ScanningCodeRes req) {
        //判断redis缓存中是否有该key值
        if (redisTemplate.hasKey("logistics_wx_login_ticket:" + req.getTicket())) {
            Map map = (Map) redisTemplate.opsForValue().get("logistics_wx_login_ticket:" + req.getTicket());
            if (null != map) {
                //从redis中取openId，然后查到微信用户信息，然后取userId,再取到userName,userPassword
                String openId = (String) map.get("openId");
                //redis序列化
                Integer status = (Integer) map.get("status");
                if (status == 1) {
                    WxUserDo wxUserDo = wxUserDao.selectOne(new QueryWrapper<WxUserDo>().eq("openid", openId));
                    UserDo userDo = dao.selectById(wxUserDo.getUserId());
                    if (userDo == null || userDo.getIsBinding() != 1) {
                        return SingleResult.failure("500", SysConstant.User_NotBind, "");
                    } else {
                        return SingleResult.success(userDo);
                    }
                }
            }
        }
        return SingleResult.success("二维码未扫描！");
    }

    @Override
    public SingleResult whetherBindingScan(ScanningCodeRes req) {
        //判断缓存里是否有保存的ticket，若存在说明二维码已扫描，反之。
        if (redisTemplate.hasKey("logistics_wx_binding_ticket:" + req.getTicket())) {
            Map map = (Map) redisTemplate.opsForValue().get("logistics_wx_binding_ticket:" + req.getTicket());
            //从redis中取openId，然后查到微信用户信息，然后取userId,再取到userName,userPassword
            if (null != map) {
                Integer status = (Integer) map.get("status");
                if (status != null) {
                    if (status == 1) {
                        return SingleResult.success(true);
                    }
                }
            }
        }
        return SingleResult.success("二维码未扫描！");
    }

    /**
     * 功能描述 推送模板消息
     *
     * @author zhujiale
     * @date 2022/12/14
     */
    @Override
    public void sendWxMsg(WxTemplatePushReq req) {
        //test使用后
        List<Long> userIdList = new ArrayList<>();
        userIdList.add(430L);
        Map<String, String> map = new HashMap<>();
        req = new WxTemplatePushReq(userIdList, new HashMap<>(), "猫爬架", 5, "2022-12-18", "江苏省苏州市腾飞创新园", "7天内一定送达", "商品出库提醒");

        String accessToken = (String) redisTemplate.opsForValue().get("logistics_wx_token");
        if (StringUtils.isBlank(accessToken)) {
            accessToken = getAccessToken();
        }
        TreeMap<String, String> params = new TreeMap<>();
        params.put("access_token", accessToken);
        // output data
        JsonObject dataAll = new JsonObject();
        //查询出要推送的用户 条件为用户已绑定微信 并且选择推送微信消息
        List<UserDo> userList = dao.selectList(new QueryWrapper<UserDo>()
                .eq("is_binding", 1)
                .in("id", req.getUserIdList()));
        //循环推送给每个符合条件的用户
        for (UserDo et : userList) {
            WxUserDo wxUserDo = wxUserDao.selectOne(new QueryWrapper<WxUserDo>().eq("user_id", et.getId()));
            if (null == wxUserDo) {
                continue;
            }
            dataAll.addProperty("touser", wxUserDo.getOpenid());
            String templateId, firstValue = null, keyword1Value = null, keyword2Value = null, keyword3Value = null, keyword4Value = null, keyword5Value = null, remark = null;
            remark = req.getRemark();
            switch (req.getTemplateType()) {
                case 1:
                    templateId = GlobalConstants.WX_PRODUCT_OUTPUT_TEMPLATE_ID;
                    firstValue = "您有新的出货通知";
                    keyword1Value = req.getProductName();
                    keyword2Value = req.getOutputNum().toString();
                    keyword3Value = req.getOutputDate();
                    keyword4Value = req.getOutputAddress();
                    keyword5Value = req.getOutputRemark();
                    break;
                case 2:
                    templateId = GlobalConstants.WX_DEAL_PROGRESS_TEMPLATE_ID;
                    firstValue = "问题咨询处理进度提醒";
                    keyword1Value = req.getServiceType();
                    keyword2Value = req.getDealStatus();
                    keyword3Value = req.getConsultContent();
                    keyword4Value = req.getWarmTip();
                    break;
                case 3:
                    templateId = GlobalConstants.WX_ORDER1_PROGRESS_TEMPLATE_ID;
                    firstValue = "您好，您的订单状态已变更";
                    keyword1Value = req.getOrderStatus1();
                    keyword2Value = req.getOrderType1();
                    keyword3Value = req.getOrderNum1();
                    keyword4Value = req.getTradingTime();
                    break;
                case 4:
                    templateId = GlobalConstants.WX_WORK_ORDER_PROGRESS_TEMPLATE_ID;
                    firstValue = "消息提醒";
                    keyword1Value = req.getWorkOrderNum();
                    keyword2Value = req.getWorkOrderDesc();
                    keyword3Value = req.getWorkOrderProgress();
                    keyword4Value = req.getWorkOrderDealPerson();
                    break;
                case 5:
                    templateId = GlobalConstants.WX_ACCOUNT_BALANCE_TEMPLATE_ID;
                    firstValue = "余额查询";
                    keyword1Value = req.getAccountBalance();
                    keyword2Value = req.getDeadline();
                    break;
                case 6:
                    templateId = GlobalConstants.WX_ORDER2_PROGRESS_TEMPLATE_ID;
                    firstValue = "您好，您的订单已支付成功，我们正在为您准备商品，尽快为您发货";
                    keyword1Value = req.getOrderStatus2();
                    keyword2Value = req.getOrderType2();
                    keyword3Value = req.getOrderAmount();
                    keyword4Value = req.getOrderDetail();
                    keyword5Value = req.getOrderNum2();
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + req.getTemplateType());
            }
            dataAll.addProperty("template_id", templateId);
            dataAll.addProperty("url", "");
            JsonObject miniprogram = new JsonObject();
            miniprogram.addProperty("appid", GlobalConstants.WX_PUSH_APPID);
            miniprogram.addProperty("pagepath", "pages/login/login?account=" + et.getUserAccount() + "&password=" + et.getUserPassword());
            dataAll.add("miniprogram", miniprogram);

            JsonObject first = new JsonObject();
            first.addProperty("value", firstValue);
            first.addProperty("color", "#173177");
            JsonObject keyword1 = new JsonObject();
            keyword1.addProperty("value", keyword1Value);
            keyword1.addProperty("color", "#173177");
            JsonObject keyword2 = new JsonObject();
            keyword2.addProperty("value", keyword2Value);
            keyword2.addProperty("color", "#173177");
            JsonObject keyword3 = new JsonObject();
            keyword3.addProperty("value", keyword3Value);
            keyword3.addProperty("color", "#173177");
            JsonObject keyword4 = new JsonObject();
            keyword4.addProperty("value", keyword4Value);
            keyword4.addProperty("color", "#173177");
            JsonObject keyword5 = new JsonObject();
            keyword5.addProperty("value", keyword5Value);
            keyword5.addProperty("color", "#173177");
            JsonObject remarkJb = new JsonObject();
            //加一个空格当没数据时 也会展示备注选项
            remarkJb.addProperty("value", remark);
            remarkJb.addProperty("color", "#173177");

            JsonObject data = new JsonObject();
            data.add("first", first);
            data.add("keyword1", keyword1);
            data.add("keyword2", keyword2);
            data.add("keyword3", keyword3);
            data.add("keyword4", keyword4);
            data.add("keyword5", keyword5);
            data.add("remark", remarkJb);
            dataAll.add("data", data);
            String result = HttpUtil.doPost("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=", params, dataAll.toString());
            String body = JSONObject.toJSONString(result);
            Object ob = JSON.parse(body);
            String s = ob.toString();
            WechatTemplateRes res = JSONObject.parseObject(s, WechatTemplateRes.class);
            //判断token是否被替换掉 替换掉拿新的token生成ticket
            Boolean bo = checkObjAllFieldsIsNull(res);
            if (!bo) {
                if (40001 == res.getErrcode()) {
                    accessToken = getAccessToken1();
                    sendWxMsg(req);
                }
            }
        }
    }

    /**
     * 判断对象中属性值是否全为空
     *
     * @param object
     * @return
     */
    public boolean checkObjAllFieldsIsNull(Object object) {
        if (null == object) {
            return true;
        }
        try {
            for (Field f : object.getClass().getDeclaredFields()) {
                f.setAccessible(true);
                if (f.get(object) != null && StringUtils.isNotBlank(f.get(object).toString())) {
                    return false;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 功能描述 根据封装的构造函数请求体判断选用那个模板
     *
     * @author zhujiale
     * @date
     */
    private String getTemplate(Integer templateType) {
        String templateId;
        switch (templateType) {
            case 1:
                templateId = GlobalConstants.WX_PRODUCT_OUTPUT_TEMPLATE_ID;
                break;
            case 2:
                templateId = GlobalConstants.WX_DEAL_PROGRESS_TEMPLATE_ID;
                break;
            case 3:
                templateId = GlobalConstants.WX_ORDER1_PROGRESS_TEMPLATE_ID;
                break;
            case 4:
                templateId = GlobalConstants.WX_WORK_ORDER_PROGRESS_TEMPLATE_ID;
                break;
            case 5:
                templateId = GlobalConstants.WX_ACCOUNT_BALANCE_TEMPLATE_ID;
                break;
            case 6:
                templateId = GlobalConstants.WX_ORDER2_PROGRESS_TEMPLATE_ID;
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + templateType);
        }
        return templateId;
    }

    /**
     * 根据appid和获取token 需要判断是否redis中logistics_wx_token是否存在
     */
    private String getAccessToken() {
        String accessToken = (String) redisTemplate.opsForValue().get("logistics_wx_token");
        if (StringUtils.isBlank(accessToken)) {
            String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + GlobalConstants.WX_APPID + "&secret=" + GlobalConstants.WX_APPSECRET;
            JsonObject accessTokenInfo = null;
            try {
                DefaultHttpClient httpClient = new DefaultHttpClient();
                HttpGet httpGet = new HttpGet(url);
                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
                String response = EntityUtils.toString(httpEntity, "utf-8");
                //LOGGER.error("获取Token-------------------"+response);
                Gson token_gson = new Gson();

                accessTokenInfo = token_gson.fromJson(response, JsonObject.class);
                System.out.println("get accessTokenInfo success. [result={" + accessTokenInfo + "}]");

                accessToken = accessTokenInfo.get("access_token").toString().replaceAll("\"", "");
                //将accesstoken存放在redis中，如果没有就重新调用方法获取,token过期时间【2小时】
                redisTemplate.opsForValue().set("logistics_wx_token", accessToken, 2, TimeUnit.HOURS);

                return accessToken;
            } catch (Exception ex) {
                System.out.println("fail to request wechat user info. [error={" + ex + "}]");
            }
        } else {
            return accessToken;
        }
        return "请求出错！";
    }

    /**
     * 根据appid和获取token  无需判断是否redis中wx_token是否存在
     */
    public String getAccessToken1() {
        String accessToken = null;
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + GlobalConstants.WX_APPID + "&secret=" + GlobalConstants.WX_APPSECRET;
        JsonObject accessTokenInfo = null;
        try {
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(url);
            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity httpEntity = httpResponse.getEntity();
            String response = EntityUtils.toString(httpEntity, "utf-8");
            //LOGGER.error("获取Token-------------------"+response);
            Gson token_gson = new Gson();

            accessTokenInfo = token_gson.fromJson(response, JsonObject.class);
            System.out.println("get accessTokenInfo success. [result={" + accessTokenInfo + "}]");

            accessToken = accessTokenInfo.get("access_token").toString().replaceAll("\"", "");
            //将accesstoken存放在redis中，如果没有就重新调用方法获取,token过期时间【2小时】
            redisTemplate.opsForValue().set("logistics_wx_token", accessToken, 2, TimeUnit.HOURS);

            return accessToken;
        } catch (Exception ex) {
            System.out.println("fail to request wechat user info. [error={" + ex + "}]");
        }
        return "请求出错！";
    }

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public SingleResult getLoginMsg(Long userId) {

        long time = new Date().getTime();
        //统计现在人数 30分钟内
        Long count30 = redisTemplate.opsForZSet().count("oms-user-online", time - 60 * 1000 * 30, time);
        Long count60 = redisTemplate.opsForZSet().count("oms-user-online", time - 60 * 1000 * 60, time);
        Long count120 = redisTemplate.opsForZSet().count("oms-user-online", time - 60 * 1000 * 120, time);

        Map<String, Object> result = new HashMap<>();
        result.put("30min-oms-user-online", count30);
        result.put("60min-oms-user-online", count60);
        result.put("120min-oms-user-online", count120);


        String[] dest = new String[30];
        for (int i = 0; i < 30; i++) {
            dest[i] = "oms-user-sign-" + DateUtils.dateFormatToString(DateUtils.passingDate(new Date(), -i), "yyyyMMdd");
        }
//        //连续7天都签到的用户
        RedisCallback<Long> redisCallback = connection -> {
            return connection.bitCount(StringUtils.join(dest, "#", 0, 6).getBytes());
//            return connection.bitOp(RedisStringCommands.BitOperation.OR,);
        };
        Long dest7DayCount = stringRedisTemplate.execute(redisCallback);
        result.put("连续7天都签到的用户数量", dest7DayCount);

        //获取指定用户近7天签到情况
        if (userId != null) {
            UserDo userDo = dao.selectById(userId);
            if (userDo != null) {
                List<String> signList = new ArrayList<>();
                for (int i = 0; i < 7; i++) {
                    List<Long> sign = redisTemplate.opsForValue().bitField(dest[i], BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(1)).valueAt(userId));
                    if (sign.size() > 0) {
                        signList.add(dest[i] + (sign.get(0) == 0L ? "未登录" : "已登录"));
                    }
                }
                result.put(userDo.getUserName() + "近七天登录情况", signList);
            }

        }

        return SingleResult.success(result);
    }
}

